package com.etheart.framework.core.fulltextsearch;

import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.search.FullTextFilter;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.wltea.analyzer.lucene.IKQueryParser;

import com.etheart.framework.core.FrameworkCoreException;
import com.etheart.framework.core.fulltextsearch.filter.Filter;

public abstract class AbstractFullTextQueryCallback<T> implements
		HibernateCallback<Object> {
	private static final Log logger = LogFactory.getLog(AbstractFullTextQueryCallback.class);

	private FullTextQueryParameter<T> param;
	protected Highlighter highlighter =null;
	protected Class<T> clazz =null;
	protected FullTextSession fullTextSession = null;
	protected Analyzer analyzer = null;
	public AbstractFullTextQueryCallback(FullTextQueryParameter<T> param) {
		this.param = param;
	}

	public Object doInHibernate(Session session) throws HibernateException,
			SQLException {

		fullTextSession = Search.getFullTextSession(session);

		
		String[] fields = param.getFields();
		String keyword = param.getKeyword();
		clazz = param.getClazz();
		if (fields == null || fields.length == 0){
			throw new IllegalArgumentException("Full text query error, you must specify field to search on");
		}


		List<org.apache.lucene.search.Query> queries = this.buildLuceneQuery(fullTextSession, clazz, fields, keyword);

		QueryBuilder qb = fullTextSession.getSearchFactory().buildQueryBuilder().forEntity(clazz).get();
		BooleanJunction<BooleanJunction> booleanJunction = qb.bool();

		for (org.apache.lucene.search.Query query : queries){
			booleanJunction.must(query);
		}
		org.apache.lucene.search.Query luceneQuery = booleanJunction.createQuery();  
       
		FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery(luceneQuery, clazz);
		SortField[] sortFields = param.getSortFields();
		if (sortFields != null && sortFields.length > 0) {
			fullTextQuery.setSort(new Sort(sortFields));
		}

		Filter[] filters = param.getFilters();
		if (filters !=null && filters.length >0){
			for (Filter filter : filters){
				String filterName = filter.getName(); 
				FullTextFilter fullTextFilter = fullTextQuery.enableFullTextFilter(filterName);
				Set<String> keys = filter.getParameterKeys();
				if (keys!=null && keys.size() > 0){
					for (String key : keys){
						fullTextFilter.setParameter(key, filter.get(key));
					}
				}
			}
		}
		
		
		if (param.isHighlight()) {
			//高亮设置  
            SimpleHTMLFormatter formatter = new SimpleHTMLFormatter( "<font color=\"red\">", "</font>");     
            QueryScorer qs = new QueryScorer(luceneQuery);  
            highlighter  = new Highlighter( formatter, qs);
		}

		return doInHibernate(fullTextQuery);
	}

	private List<org.apache.lucene.search.Query> buildLuceneQuery(FullTextSession fullTextSession, Class<T> clazz, String[] fields, String keyword){
		analyzer = fullTextSession.getSearchFactory().getAnalyzer(clazz);
	
		List<org.apache.lucene.search.Query> queries = new ArrayList<org.apache.lucene.search.Query>();
	
		keyword = escape(keyword);
		List<String> words = analyzer(analyzer, keyword);
		if (logger.isDebugEnabled()) {
			logger.debug(words.toString());
		}
		StringBuffer stb = new StringBuffer();
		Occur[] flags = new Occur[fields.length];
		for (int i = 0; i < flags.length; i++) {
			flags[i] = Occur.SHOULD;
		}
		Query query = null;
		try {
			for (String w : words) {
				query = IKQueryParser.parseMultiField(fields, w, flags);
				queries.add(query);
			}
			System.out.println("");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return queries;
	}

	private String escape(String matching){
		String matchingStr = matching.toLowerCase();
		String escaped = MultiFieldQueryParser.escape(matchingStr);
		return escaped;
	}
	private TokenStream getTokenStream(Analyzer analyzer, String keyword) {
		return analyzer.tokenStream("", new StringReader(keyword));
	}

	private List<String> analyzer(Analyzer analyzer, String keyword) {
		TokenStream ts = getTokenStream(analyzer, keyword);
		CharTermAttribute termAtt = (CharTermAttribute) ts.getAttribute(CharTermAttribute.class);
		List<String> analyzerResult = new ArrayList<String>();
		try {
	      while (ts.incrementToken()) {
	        String token = new String(termAtt.buffer(),0,termAtt.length());
	        analyzerResult.add(token);
	      }
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return analyzerResult;
	}
	
	private static int HIGHLIGHT_MAX_LENGTH = 20;

	protected void highlighteHandler(List<T> list) throws IllegalArgumentException, IllegalAccessException, IOException, InvalidTokenOffsetsException {
		if (highlighter==null || clazz ==null || analyzer == null) {
			throw new FrameworkCoreException("高亮显示初始化错误:highlighter:"+highlighter+",clazz:"+clazz+",analyzer:"+analyzer);
		}
		Field[] fields = clazz.getDeclaredFields();
		List<Field> fieldList = new ArrayList<Field>();
		
		// 找到所有需要高亮的字段

		for (Field field : fields) {
			if (field.getAnnotation(Highlight.class) != null) {
				fieldList.add(field);
				continue;
			}
		}

		// 关键内容显示大小设置
		
	      highlighter.setTextFragmenter(new SimpleFragmenter(HIGHLIGHT_MAX_LENGTH));
		//高亮字段 
		for (Object t : list) {
			for (Field field : fieldList) {
				field.setAccessible(true);
				if (!field.getType().equals(String.class)) {
					//非string暂时不考虑
					continue;
				}
				String text = (String) field.get(t);
				if (text == null) {
					field.setAccessible(false);
					continue;
				}
			//	TokenStream tokenStream =getTokenStream(analyzer, text);
				String newText = highlighter.getBestFragment(analyzer, field.getName(), text);
				if (newText == null) {//如果找不到高亮的词
					Highlight hl =field.getAnnotation(Highlight.class);
					if (hl.showFull()) {
						newText =text;
					}else{
						newText = text.substring(0,HIGHLIGHT_MAX_LENGTH);
					}
				}
				field.set(t, newText);
				field.setAccessible(false);
			}
		}
	}
	
	protected abstract Object doInHibernate(FullTextQuery query);
}
